home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / stdwin / Ports / vtrm / vtrm.c < prev   
Encoding:
C/C++ Source or Header  |  1992-01-23  |  45.8 KB  |  2,118 lines  |  [TEXT/????]

  1. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1991. */
  2.  
  3. /*
  4.  * Virtual TeRMinal package.
  5.  * (For a description see at the end of this file.)
  6.  *
  7.  * Big change:
  8.  *    output goes to /dev/tty, in case stdout is redirected to a file.
  9.  *
  10.  * TO DO:
  11.  *    - add interrupt handling (trminterrupt)
  12.  *    - adapt to changed window size when suspended or at SIGWINCH
  13.  *      (unfortunately, the calling module must be changed first --
  14.  *      it is not prepared for the changed window size...)
  15.  */
  16.  
  17. /* Includes: */
  18.  
  19. #include "os.h"    /* operating system features and C compiler dependencies */
  20. #include "vtrm.h"  /* terminal capabilities */
  21.  
  22. #ifndef HAS_TERMIO
  23. #include <sgtty.h>
  24. #else
  25. #include <termio.h>
  26. #endif
  27.  
  28. #ifdef SIGNAL
  29. #include <signal.h>
  30. #endif
  31.  
  32. #ifdef SETJMP
  33. #include <setjmp.h>
  34. #endif
  35.  
  36. #ifdef HAS_SELECT
  37. #include <sys/time.h>
  38. #endif
  39.  
  40. /* Style definitions: */
  41.  
  42. #define Visible
  43. #define Hidden static
  44. #define Procedure
  45.  
  46. typedef int bool;
  47. typedef char *string;
  48. typedef short intlet;
  49.  
  50. #define Yes ((bool) 1)
  51. #define No  ((bool) 0)
  52.  
  53. /* Forward declarations: */
  54.  
  55. extern char *malloc();
  56. extern char *getenv();
  57. extern int tgetent();
  58. extern int tgetnum();
  59. extern int tgetflag();
  60. extern char *tgetstr();
  61. extern char *tgoto();
  62.  
  63. Hidden int getttyfp();
  64. Hidden int str0cost();
  65. Hidden int gettermcaps();
  66. Hidden int setttymode();
  67. Hidden Procedure resetttymode();
  68. Hidden int start_trm();
  69. Hidden bool get_pos();
  70. Hidden int put_line();
  71. Hidden Procedure set_mode();
  72. Hidden Procedure get_so_mode();
  73. Hidden Procedure standout();
  74. Hidden Procedure standend();
  75. Hidden Procedure put_str();
  76. Hidden Procedure ins_str();
  77. Hidden Procedure del_str();
  78. Hidden Procedure put_c();
  79. Hidden Procedure clear_lines();
  80. Hidden Procedure clr_to_eol();
  81. Hidden int outchar();
  82. Hidden Procedure set_blanks();
  83. Hidden Procedure scr1up();
  84. Hidden Procedure scr1down();
  85. Hidden Procedure scr2up();
  86. Hidden Procedure scr2down();
  87. Hidden Procedure scr_lines();
  88. Hidden Procedure addlines();
  89. Hidden Procedure dellines();
  90. Hidden Procedure scr3up();
  91. Hidden Procedure lf_scroll();
  92. Hidden Procedure move_lines();
  93. Hidden Procedure trmpushback();
  94. Hidden Procedure subshell();
  95.  
  96. /* Data definitions: */
  97.  
  98. #ifdef VTRMTRACE
  99. #define TRACEFILE "trace.vtrm"
  100. Hidden FILE *vtrmfp= (FILE *) NULL;
  101. #endif
  102.  
  103. #define Min(a,b) ((a) <= (b) ? (a) : (b))
  104.  
  105. /* tty modes */
  106.  
  107. #ifndef HAS_TERMIO
  108.  
  109. /* v7/BSD tty control */
  110. Hidden struct sgttyb oldtty, newtty;
  111.  
  112. #ifdef TIOCSETN
  113. /* Redefine stty to use TIOCSETN, so type-ahead is not flushed */
  114. #define stty(fd, bp) VOID ioctl(fd, TIOCSETN, (char *) bp)
  115. #endif /* TIOCSETN */
  116.  
  117. #ifdef TIOCSLTC /* BSD -- local special chars, must all be turned off */
  118. Hidden struct ltchars oldltchars;
  119. Hidden struct ltchars newltchars= {-1, -1, -1, -1, -1, -1};
  120. #endif /* TIOCSLTC */
  121.  
  122. #ifdef TIOCSETC /* V7 -- standard special chars, some must be turned off too */
  123. Hidden struct tchars oldtchars;
  124. Hidden struct tchars newtchars;
  125. #endif /* TIOCSETC */
  126.  
  127. #else /* HAS_TERMIO */
  128.  
  129. /* AT&T tty control */
  130. Hidden struct termio oldtty, newtty, polltty;
  131. #define gtty(fd,bp) ioctl(fd, TCGETA, (char *) bp)
  132. #define stty(fd,bp) VOID ioctl(fd, TCSETAW, (char *) bp)
  133.  
  134. #endif /* HAS_TERMIO */
  135.  
  136. /* If you can't lookahead in the system's input queue, and so can't implement
  137.  * trmavail(), you have to enable keyboard interrupts.
  138.  * Otherwise, computations are uninterruptable.
  139.  */
  140. #ifndef HAS_TERMIO
  141. #ifndef HAS_SELECT 
  142. #ifdef SIGNAL
  143. #define CATCHINTR
  144. #endif
  145. #endif
  146. #endif
  147.  
  148. Hidden bool know_ttys = No;
  149.  
  150. /* visible data for termcap */
  151. char PC;
  152. char *BC;
  153. char *UP;
  154. short ospeed;
  155.  
  156. Hidden FILE *fp= (FILE *) NULL;
  157. #define Putstr(str)    tputs((str), 1, outchar)
  158.  
  159. /* termcap terminal capabilities */
  160.  
  161. Hidden int lines;
  162. Hidden int cols;
  163.  
  164. /*
  165.  * String-valued capabilities are saved in one big array.
  166.  * Extend this only at the end (even though it disturbs the sorting)
  167.  * because otherwise you have to change all macros...
  168.  */
  169.  
  170. #define par_al_str strcaps[0]    /* parametrized al (AL) */
  171. #define cap_cm_str strcaps[1]    /* screen-relative cursor motion (CM) */
  172. #define par_dl_str strcaps[2]    /* parametrized dl (DL) */
  173. #define al_str strcaps[3]     /* add new blank line */
  174. #define cd_str strcaps[4]     /* clear to end of display */
  175. #define ce_str strcaps[5]     /* clear to end of line */
  176. #define cl_str strcaps[6]     /* cursor home and clear screen */
  177. #define cm_str strcaps[7]     /* cursor motion */
  178. #define cp_str strcaps[8]    /* cursor position sense reply; obsolete */
  179. #define cr_str strcaps[9]     /* carriage return */
  180. #define cs_str strcaps[10]     /* change scrolling region */
  181. #define dc_str strcaps[11]     /* delete character */
  182. #define dl_str strcaps[12]     /* delete line */
  183. #define dm_str strcaps[13]     /* enter delete mode */
  184. #define do_str strcaps[14]     /* cursor down one line */
  185. #define ed_str strcaps[15]     /* end delete mode */
  186. #define ei_str strcaps[16]     /* end insert mode */
  187. #define ho_str strcaps[17]    /* cursor home */
  188. #define ic_str strcaps[18]     /* insert character (if necessary; may pad) */
  189. #define im_str strcaps[19]     /* enter insert mode */
  190. #define nd_str strcaps[20]     /* cursor right (non-destructive space) */
  191. #define nl_str strcaps[21]    /* newline */
  192. #define se_str strcaps[22]     /* end standout mode */
  193. #define sf_str strcaps[23]     /* scroll text up (from bottom of region) */
  194. #define so_str strcaps[24]     /* begin standout mode */
  195. #define sp_str strcaps[25]    /* sense cursor position; obsolete */
  196. #define sr_str strcaps[26]     /* scroll text down (from top of region) */
  197. #define te_str strcaps[27]     /* end termcap */
  198. #define ti_str strcaps[28]     /* start termcap */
  199. #define vb_str strcaps[29]     /* visible bell */
  200. #define ve_str strcaps[30]     /* make cursor visible again */
  201. #define vi_str strcaps[31]     /* make cursor invisible */
  202. #define le_str strcaps[32]     /* cursor left */
  203. #define bc_str strcaps[33]    /* backspace character */
  204. #define up_str strcaps[34]     /* cursor up */
  205. #define pc_str strcaps[35]    /* pad character */
  206. #define ks_str strcaps[36]    /* keypad mode start */
  207. #define ke_str strcaps[37]    /* keypad mode end */
  208. #define us_str strcaps[38]    /* start underscore mode */
  209. #define ue_str strcaps[39]    /* end underscore mode */
  210. /* Insert new entries here only! Don't forget to change the next line! */
  211. #define NSTRCAPS 40 /* One more than the last entry's index */
  212.  
  213. Hidden char *strcaps[NSTRCAPS];
  214. Hidden char strcapnames[] =
  215. "ALCMDLalcdceclcmcpcrcsdcdldmdoedeihoicimndnlsesfsospsrtetivbvevilebcuppckskeusue";
  216.  
  217. /* Same for Boolean-valued capabilities */
  218.  
  219. #define has_am flagcaps[0]    /* has automatic margins */
  220. #define has_da flagcaps[1]    /* display may be retained above screen */
  221. #define has_db flagcaps[2]    /* display may be retained below screen */
  222. #define has_in flagcaps[3]    /* not safe to have null chars on the screen */
  223. #define has_mi flagcaps[4]    /* move safely in insert (and delete?) mode */
  224. #define has_ms flagcaps[5]    /* move safely in standout mode */
  225. #define has_xs flagcaps[6]    /* standout not erased by overwriting */
  226. #define has_bs flagcaps[7]    /* terminal can backspace */
  227. #define hardcopy flagcaps[8]    /* hardcopy terminal */
  228. #define has_xn flagcaps[9]    /* Vt100 / Concept glitch */
  229. #define NFLAGS 10
  230.  
  231. Hidden char flagcaps[NFLAGS];
  232. Hidden char flagnames[]= "amdadbinmimsxsbshcxn";
  233.  
  234. Hidden Procedure getcaps(parea)
  235.      register char **parea;
  236. {
  237.     register char *capname;
  238.     register char **capvar;
  239.     register char *flagvar;
  240.  
  241.     for (capname= flagnames, flagvar= flagcaps;
  242.             *capname != '\0'; capname += 2, ++flagvar)
  243.         *flagvar= tgetflag(capname);
  244.  
  245.     for (capname= strcapnames, capvar= strcaps;
  246.             *capname != '\0'; capname += 2, ++capvar)
  247.         *capvar= tgetstr(capname, parea);
  248. }
  249.  
  250. /* terminal status */
  251.  
  252. /* calling order of Visible Procs */
  253. Hidden bool started = No;
  254.  
  255. /* to exports the capabilities mentioned in vtrm.h: */
  256. Hidden int flags = 0;
  257.  
  258. /* cost for impossible operations */
  259. #define Infinity 9999
  260.     /* Allow for adding Infinity+Infinity within range */
  261.     /* (Range is assumed at least 2**15 - 1) */
  262.  
  263. /* The following for all sorts of undefined things (except for UNKNOWN char) */
  264. #define Undefined (-1)
  265.  
  266. /* current mode of putting char's */
  267. #define Normal    0
  268. #define Insert    1
  269. #define    Delete    2
  270. Hidden short mode = Normal;
  271.  
  272. /* masks for char's */
  273. #define NULCHAR    '\000'
  274. #define UNKNOWN    1
  275. #define SOBIT    1
  276. /* if (has_xs) record cookies placed on screen in extra bit */
  277. /* type of cookie is determined by the SO bit */
  278. #define XSBIT    2
  279. #define SOCOOK    3
  280. #define SECOOK    2
  281. #define COOKBITS SOCOOK
  282. #define NOCOOK    0
  283.  
  284. /* current standout mode */
  285. #define Off    0
  286. #define On    SOBIT
  287. Hidden short so_mode = Off;
  288.  
  289. /* current cursor position */
  290. Hidden short cur_y = Undefined, cur_x = Undefined;
  291.  
  292. /* "linedata[y][x]" holds the char on the terminal;
  293.  * "linemode[y][x]" holds the SOBIT and XSBIT.
  294.  * the SOBIT tells whether the character is standing out, the XSBIT whether
  295.  * there is a cookie on the screen at this position.
  296.  * In particular a standend-cookie may be recorded AFTER the line
  297.  * (just in case some trmputdata will write after that position).
  298.  * "lenline[y]" holds the length of the line.
  299.  * Unknown chars will be 1, so the optimising compare in putline will fail.
  300.  * (Partially) empty lines are distinghuished by "lenline[y] < cols".
  301.  */
  302. Hidden char **linedata = NULL, **linemode = NULL;
  303. Hidden intlet *lenline = NULL;
  304.  
  305. /* To compare the mode part of the line when the
  306.  * mode parameter of trmputdata == NULL, we use the following:
  307.  */
  308. Hidden char plain[1]= {PLAIN};
  309.  
  310. /* Clear the screen initially iff only memory cursor addressing available */
  311. Hidden bool mustclear = No;
  312.  
  313. /* Make the cursor invisible when trmsync() tries to move outside the screen */
  314. Hidden bool no_cursor = No;
  315.  
  316. /* Optimise cursor motion */
  317. Hidden int abs_cost;         /* cost of absolute cursor motion */
  318. Hidden int cr_cost;         /* cost of carriage return */
  319. Hidden int do_cost;         /* cost of down */
  320. Hidden int le_cost;         /* cost of left */
  321. Hidden int nd_cost;         /* cost of right */
  322. Hidden int up_cost;         /* cost of up */
  323.  
  324. /* Optimise trailing match in put_line, iff the terminal can insert and delete
  325.  * characters; the cost per n characters will be:
  326.  *     n * MultiplyFactor + OverHead
  327.  */
  328. Hidden int ins_mf, ins_oh, del_mf, del_oh;
  329. Hidden int ed_cost, ei_cost;         /* used in move() */
  330.  
  331. /* The type of scrolling possible determines which routines get used;
  332.  * these may be:
  333.  * (1) with addline and deleteline (termcap: al_str & dl_str);
  334.  * (2) with a settable scrolling region, like VT100 (cs_str, sr_str, sf_str);
  335.  * (3) no hardware scrolling available.
  336.  */
  337. Hidden Procedure (*scr_up)();
  338. Hidden Procedure (*scr_down)();
  339. Hidden bool canscroll= Yes;
  340.  
  341. /*
  342.  * Starting, Ending and (fatal) Error.
  343.  */
  344.  
  345. /* 
  346.  * Initialization call.
  347.  * Determine terminal capabilities from termcap.
  348.  * Set up tty modes.
  349.  * Start up terminal and internal administration.
  350.  * Return 0 if all well, error code if in trouble.
  351.  */
  352. Visible int trmstart(plines, pcols, pflags)
  353.      int *plines;
  354.      int *pcols;
  355.      int *pflags;
  356. {
  357.     register int err;
  358.  
  359. #ifdef VTRMTRACE
  360.     if (!started) vtrmfp= fopen(TRACEFILE, vtrmfp ? "a" : "w");
  361.     if (vtrmfp) fprintf(vtrmfp, "\ttrmstart(&li, &co, &fl);\n");
  362. #endif
  363.     if (started)
  364.         return TE_TWICE;
  365.     err= getttyfp();
  366.     if (err != TE_OK)
  367.         return err;
  368.     err= gettermcaps();
  369.     if (err != TE_OK)
  370.         return err;
  371.     err= setttymode();
  372.     if (err != TE_OK)
  373.         return err;
  374.     err= start_trm();
  375.     if (err != TE_OK) {
  376.         trmend();
  377.         return err;
  378.     }
  379.  
  380.     *plines = lines;
  381.     *pcols = cols;
  382.     *pflags = flags;
  383.  
  384.     started = Yes;
  385.     
  386.     trmsync(lines-1, 0);   /* position to end of screen */
  387.     
  388.     return TE_OK;
  389. }
  390.  
  391. /*
  392.  * Termination call.
  393.  * Reset tty modes, etc.
  394.  * Beware that it might be called by a caught interrupt even in the middle
  395.  * of trmstart()!
  396.  */
  397. Visible Procedure trmend()
  398. {
  399. #ifdef VTRMTRACE
  400.     if (vtrmfp) fprintf(vtrmfp, "\ttrmend();\n");
  401. #endif
  402.     set_mode(Normal);
  403.     if (so_mode != Off)
  404.         standend();
  405.     Putstr(ke_str);
  406.     Putstr(te_str);
  407.     VOID fflush(fp);
  408.     resetttymode();
  409.  
  410.     started = No;
  411. #ifdef VTRMTRACE
  412.     if (vtrmfp) fclose(vtrmfp);
  413. #endif
  414. }
  415.  
  416. /*
  417.  * Set all internal statuses to undefined, especially the contents of
  418.  * the screen, so a hard redraw will not be optimised to heaven.
  419.  */
  420. Visible Procedure trmundefined()
  421. {
  422.     register int y, x;
  423. #ifdef VTRMTRACE
  424.     if (vtrmfp) fprintf(vtrmfp, "\ttrmundefined();\n");
  425. #endif
  426.  
  427.     cur_y = cur_x = Undefined;
  428.     mode = so_mode = Undefined;
  429.     
  430.     for (y = 0; y < lines; y++) {
  431.         for (x = 0; x <= cols; x++) {
  432.             linedata[y][x] = UNKNOWN; /* impossible char */
  433.             linemode[y][x] = NOCOOK;  /* no so bits */
  434.         }
  435.         lenline[y] = cols;
  436.     }
  437. }
  438.  
  439. #ifdef VTRMTRACE
  440.  
  441. /*ARGSUSED*/
  442. Hidden Procedure check_started(m)
  443.      char *m;
  444. {
  445.     if (!started) {
  446.         trmend();
  447.         if (vtrmfp) fprintf(vtrmfp, "bad VTRM call\n");
  448.         abort();
  449.     }
  450. }
  451.  
  452. #else
  453.  
  454. #define check_started(m) /*empty*/
  455.  
  456. #endif /* VTRMTRACE */
  457.  
  458. Hidden int getttyfp()
  459. {
  460.     if (fp != NULL)    /* already initialised */
  461.         return TE_OK;
  462.     fp= fopen("/dev/tty", "w");
  463.     if (fp == NULL)
  464.         return TE_NOTTY;
  465.     return TE_OK;
  466. }
  467.  
  468. Hidden int ccc;
  469.  
  470. /*ARGSUSED*/
  471. Hidden Procedure countchar(ch)
  472.      char ch;
  473. {
  474.     ccc++;
  475. }
  476.  
  477. Hidden int strcost(str)
  478.      char *str;
  479. {
  480.     if (str == NULL)
  481.         return Infinity;
  482.     return str0cost(str);
  483. }
  484.  
  485. Hidden int str0cost(str)
  486.      char *str;
  487. {
  488.     ccc = 0;
  489.     tputs(str, 1, countchar);
  490.     return ccc;
  491. }
  492.  
  493. /*
  494.  * Get terminal capabilities from termcap and compute related static
  495.  * properties.  Return TE_OK if all well, error code otherwise.
  496.  */
  497.  
  498. Hidden int gettermcaps()
  499. {
  500.     string trmname;
  501.     char tc_buf[1024];
  502.     static char strbuf[1024];
  503.     char *area = strbuf;
  504.     int sg;
  505.     static bool tc_initialized = No;
  506.  
  507.     if (tc_initialized)
  508.         return TE_OK;
  509.     
  510.     trmname=getenv("TERM");
  511.     if (trmname == NULL || trmname[0] == '\0')
  512.         return TE_NOTERM;
  513.     if (tgetent(tc_buf, trmname) != 1)
  514.         return TE_BADTERM;
  515.  
  516.     getcaps(&area); /* Read all flag and string type capabilities */
  517.     if (hardcopy)
  518.         return TE_DUMB;
  519.     BC = le_str;
  520.     if (BC == NULL) {
  521.         BC = bc_str;
  522.         if (BC == NULL) {
  523.             if (has_bs)
  524.                 BC = "\b";
  525.             else
  526.                 return TE_DUMB;
  527.         }
  528.     }
  529.     UP = up_str;
  530.     if (UP == NULL)
  531.         return TE_DUMB;
  532.     PC = (pc_str != NULL? pc_str[0] : NULCHAR);
  533.  
  534.     if (cm_str == NULL) {
  535.         cm_str = cap_cm_str;
  536.         if (cm_str == NULL) {
  537.             if (ho_str == NULL || do_str == NULL || nd_str == NULL)
  538.                 return TE_DUMB;
  539.         }
  540.         else
  541.             mustclear = Yes;
  542.     }
  543.     if (al_str && dl_str) {
  544.         scr_up = scr1up;
  545.         scr_down = scr1down;
  546.         flags |= CAN_SCROLL;
  547.     }
  548.     else {
  549.         if (sf_str == NULL)
  550.             sf_str = "\n";
  551.         if (cs_str && sr_str) {
  552.             scr_up = scr2up;
  553.             scr_down = scr2down;
  554.             flags |= CAN_SCROLL;
  555.         }
  556.         else {
  557.             canscroll= No;
  558.         }
  559.     }
  560.         
  561.     lines = tgetnum("li");
  562.     cols = tgetnum("co");
  563.     if (lines <= 0) lines = 24;
  564.     if (cols <= 0) cols = 80;
  565.     
  566.     if (!ce_str)
  567.         return TE_DUMB;
  568.     if (cr_str == NULL) cr_str = "\r";
  569.     if (do_str == NULL) {
  570.         do_str = nl_str;
  571.         if (do_str == NULL) do_str = "\n";
  572.     }
  573.     le_str = BC;
  574.     up_str = UP;
  575.     if (vb_str == NULL)     /* then we will do with the audible bell */
  576.         vb_str = "\007";
  577.     
  578.     if (so_str != NULL && se_str != NULL && (sg=tgetnum("sg")) <= 0) {
  579.         if (sg == 0)
  580.             has_xs = Yes;
  581.         flags |= HAS_STANDOUT;
  582.     }
  583.     else if (us_str != NULL && ue_str != NULL) {
  584.         so_str = us_str; se_str = ue_str;
  585.         flags |= HAS_STANDOUT;
  586.     }
  587.     else
  588.         return TE_DUMB;
  589.  
  590.     /* calculate costs of local and absolute cursor motions */
  591.     if (cm_str == NULL)
  592.         abs_cost = Infinity;
  593.     else
  594.         abs_cost = strcost(tgoto(cm_str, 0, 0));
  595.     cr_cost = strcost(cr_str);
  596.     do_cost = strcost(do_str);
  597.     le_cost = strcost(le_str);
  598.     nd_cost = strcost(nd_str);
  599.     up_cost = strcost(up_str);
  600.  
  601.     /* cost of leaving insert or delete mode, used in move() */
  602.     ei_cost = str0cost(ei_str);
  603.     ed_cost = str0cost(ed_str);
  604.     
  605.     /* calculate insert and delete cost multiply_factor and overhead */
  606.     if (((im_str && ei_str) || ic_str) && dc_str) {
  607.         flags |= CAN_OPTIMISE;
  608.         ins_mf = 1 + str0cost(ic_str);
  609.         ins_oh = str0cost(im_str) + ei_cost;
  610.         del_mf = str0cost(dc_str);
  611.         del_oh = str0cost(dm_str) + ed_cost;
  612.     }
  613.         
  614.     tc_initialized = Yes;
  615.     return TE_OK;
  616. }
  617.  
  618. #ifdef CATCHINTR
  619.  
  620. Hidden char intrchar;
  621. Hidden bool trmintrptd = No;
  622. Hidden bool readintrcontext = No;
  623. #ifdef SETJMP
  624. Hidden jmp_buf readinterrupt;
  625. #endif
  626.  
  627. Hidden SIGTYPE trmintrhandler(sig)
  628.      int sig;
  629. {
  630.     VOID signal(SIGINT, trmintrhandler);
  631.     trmintrptd = Yes;
  632. #ifdef SETJMP
  633.     if (readintrcontext) longjmp(readinterrupt, 1);
  634. #endif
  635. }
  636.  
  637. #endif /* CATCHINTR */
  638.  
  639. Hidden int setttymode()
  640. {
  641.     if (!know_ttys) {
  642.         if (gtty(0, &oldtty) != 0
  643.             || gtty(0, &newtty) != 0
  644. #ifdef HAS_TERMIO
  645.             || gtty(0, &polltty) != 0
  646. #endif
  647.             )
  648.             return TE_NOTTY;
  649. #ifndef HAS_TERMIO
  650.         ospeed = oldtty.sg_ospeed;
  651.         newtty.sg_flags = (newtty.sg_flags & ~ECHO & ~CRMOD & ~XTABS)
  652.                   | CBREAK;
  653. #ifdef TIOCSLTC
  654.         VOID ioctl(0, TIOCGLTC, (char *) &oldltchars);
  655. #endif
  656. #ifdef TIOCSETC
  657.         VOID ioctl(0, TIOCGETC, (char *) &oldtchars);
  658. #endif
  659.  
  660. #else /* HAS_TERMIO */
  661.         ospeed= oldtty.c_lflag & CBAUD;
  662.         newtty.c_iflag &= ~ICRNL; /* No CR->NL mapping on input */
  663.         newtty.c_oflag &= ~ONLCR; /* NL doesn't output CR */
  664.         newtty.c_lflag &= ~(ICANON|ECHO|ISIG);
  665.                 /* No line editing, no echo, 
  666.                  * no quit, intr or susp signals */
  667.         newtty.c_cc[VMIN]= 3; /* wait for 3 characters */
  668.         newtty.c_cc[VTIME]= 1; /* or 0.1 sec. */
  669.  
  670.         polltty.c_iflag= newtty.c_iflag;
  671.         polltty.c_oflag= newtty.c_oflag;
  672.         polltty.c_lflag= newtty.c_lflag;
  673.         polltty.c_cc[VQUIT]= newtty.c_cc[VQUIT];
  674. #ifdef VSUSP
  675.         polltty.c_cc[VSUSP]= newtty.c_cc[VSUSP];
  676. #endif
  677.         polltty.c_cc[VMIN]= 0;  /* don't block read() */
  678.         polltty.c_cc[VTIME]= 0; /* just give 'em chars iff available */
  679. #endif /* HAS_TERMIO */
  680.         know_ttys = Yes;
  681.     }
  682.     stty(0, &newtty);
  683. #ifndef HAS_TERMIO
  684. #ifdef TIOCSLTC
  685.     VOID ioctl(0, TIOCSLTC, (char *) &newltchars);
  686. #endif
  687. #ifdef TIOCSETC
  688.     VOID ioctl(0, TIOCGETC, (char *) &newtchars);
  689. #ifndef CATCHINTR
  690.     newtchars.t_intrc= -1;
  691. #else
  692.     intrchar= oldtchars.t_intrc;
  693.     newtchars.t_intrc= intrchar;   /* do not disable interupt */
  694.     signal(SIGINT, trmintrhandler);/* but catch it */
  695. #endif /* CATCHINTR */
  696.     newtchars.t_quitc= -1;
  697.     newtchars.t_eofc= -1;
  698.     newtchars.t_brkc= -1;
  699.     VOID ioctl(0, TIOCSETC, (char *) &newtchars);
  700. #endif /* TIOCSETC */
  701. #endif /* !HAS_TERMIO */
  702.     return TE_OK;
  703. }
  704.  
  705. Hidden Procedure resetttymode()
  706. {
  707.     if (know_ttys) {
  708.         stty(0, &oldtty);
  709. #ifndef HAS_TERMIO
  710. #ifdef TIOCSLTC
  711.         VOID ioctl(0, TIOCSLTC, (char *) &oldltchars);
  712. #endif
  713. #ifdef TIOCSETC
  714.         VOID ioctl(0, TIOCSETC, (char *) &oldtchars);
  715. #endif
  716. #endif /* !HAS_TERMIO */
  717.         know_ttys= No;
  718.     }
  719. }
  720.  
  721. Hidden int start_trm()
  722. {
  723.     register int y;
  724. #ifdef TIOCGWINSZ
  725.     struct winsize win;
  726.  
  727.     if (ioctl(0, TIOCGWINSZ, (char*)&win) == 0) {
  728.         if (win.ws_col > 0 && ((int) win.ws_col) != cols
  729.             ||
  730.             win.ws_row > 0 && ((int) win.ws_row) != lines) {
  731.             /* Window size has changed.
  732.                Release previously allocated buffers. */
  733.             if (linedata != NULL) {
  734.                 for (y= 0; y < lines; ++y) {
  735.                     free((char *) linedata[y]);
  736.                 }
  737.                 free((char *) linedata);
  738.                 linedata= NULL;
  739.             }
  740.             if (linemode != NULL) {
  741.                 for (y= 0; y < lines; ++y) {
  742.                     free((char *) linemode[y]);
  743.                 }
  744.                 free((char *) linemode);
  745.                 linemode= NULL;
  746.             }
  747.             if (lenline != NULL) {
  748.                 free((char *) lenline);
  749.                 lenline= NULL;
  750.             }
  751.         }
  752.         if (((int)win.ws_col) > 0)
  753.             cols = win.ws_col;
  754.         if (((int)win.ws_row) > 0)
  755.             lines = win.ws_row;
  756.     }
  757. #endif
  758.     if (linedata == NULL) {
  759.         if ((linedata = (char**) malloc(lines * sizeof(char*))) == NULL)
  760.             return TE_NOMEM;
  761.         for (y = 0; y < lines; y++) {
  762.             if ((linedata[y] = (char*) malloc((cols+1) * sizeof(char))) == NULL)
  763.                 return TE_NOMEM;
  764.         }
  765.     }
  766.     if (linemode == NULL) {
  767.         if ((linemode = (char**) malloc(lines * sizeof(char*))) == NULL)
  768.             return TE_NOMEM;
  769.         for (y = 0; y < lines; y++) {
  770.             if ((linemode[y] = (char*) malloc((cols+1) * sizeof(char))) == NULL)
  771.                 return TE_NOMEM;
  772.         }
  773.     }
  774.     if (lenline == NULL) {
  775.         if ((lenline = (intlet*) malloc(lines * sizeof(intlet))) == NULL)
  776.             return TE_NOMEM;
  777.     }
  778.  
  779.     trmundefined();
  780.  
  781.     Putstr(ti_str);
  782.     Putstr(ks_str);
  783.     if (cs_str)
  784.         Putstr(tgoto(cs_str, lines-1, 0));
  785.     if (mustclear)
  786.         clear_lines(0, lines-1);
  787.     VOID fflush(fp);
  788.     
  789.     return TE_OK;
  790. }
  791.  
  792.  
  793. /*
  794.  * Sensing and moving the cursor.
  795.  */
  796.  
  797. /*
  798.  * Sense the current (y, x) cursor position.
  799.  * On terminals with local cursor motion, the first argument must be the
  800.  * string that must be sent to the terminal to ask for the current cursor
  801.  * position after a possible manual change by the user;
  802.  * the format describes the answer as a parameterized string
  803.  * a la termcap(5).
  804.  * If the terminal cannot be asked for the current cursor position,
  805.  * or if the string returned by the terminal is garbled,
  806.  * the position is made Undefined.
  807.  * This scheme can also be used for mouse clicks, if these can be made to
  808.  * send the cursor position; the sense string can normally be empty.
  809.  */
  810.  
  811. Visible Procedure trmsense(sense, format, py, px)
  812.      string sense;
  813.      string format;
  814.      int *py;
  815.      int *px;
  816. {
  817. #ifdef VTRMTRACE
  818.     if (vtrmfp) fprintf(vtrmfp, "\ttrmsense(&yy, &xx);\n");
  819. #endif
  820.     check_started("trmsense");
  821.  
  822.     if (sense != NULL)
  823.         Putstr(sense);
  824.     VOID fflush(fp);
  825.  
  826.     *py = *px = Undefined;
  827.     set_mode(Normal);
  828.     if (so_mode != Off)
  829.         standend();
  830.     
  831.     if (format != NULL && get_pos(format, py, px)) {
  832.         if (*py < 0 || lines <= *py || *px < 0 || cols <= *px)
  833.             *py = *px = Undefined;
  834.     }
  835.     cur_y = Undefined;
  836.     cur_x = Undefined;
  837. }
  838.  
  839. Hidden bool get_pos(format, py, px)
  840.      string format;
  841.      int *py;
  842.      int *px;
  843. {
  844.     int fc;         /* current format character */
  845.     int ic;         /* current input character */
  846.     int num;
  847.     int on_y = 1;
  848.     bool incr_orig = No;
  849.     int i, ni;
  850.  
  851.     while (fc = *format++) {
  852.         if (fc != '%') {
  853.             if (trminput() != fc)
  854.                 return No;
  855.         }
  856.         else {
  857.             switch (fc = *format++) {
  858.             case '%':
  859.                 if (trminput() != '%')
  860.                     return No;
  861.                 continue;
  862.             case '.':
  863.                 VOID trminput(); /* skip one char */
  864.                 continue;
  865.             case 'r':
  866.                 on_y = 1 - on_y;
  867.                 continue;
  868.             case 'i':
  869.                 incr_orig = Yes;
  870.                 continue;
  871.             case 'd':
  872.                 ic = trminput();
  873.                 if (!isdigit(ic))
  874.                     return No;
  875.                 num = ic - '0';
  876.                 while (isdigit(ic=trminput()))
  877.                     num = 10*num + ic - '0';
  878.                 trmpushback(ic);
  879.                 break;
  880.             case '2':
  881.             case '3':
  882.                 ni = fc - '0';
  883.                     num = 0;
  884.                 for (i=0; i<ni; i++) {
  885.                     ic = trminput();
  886.                     if (isdigit(ic))
  887.                         num = 10*num + ic - '0';
  888.                     else
  889.                         return No;
  890.                 }
  891.                 break;
  892.             case '+':
  893.                 num = trminput() - *format++;
  894.                 break;
  895.             case '-':
  896.                 num = trminput() + *format++;
  897.                 break;
  898.             default:
  899.                 return No;
  900.             }
  901.             /* assign num to parameter */
  902.             if (incr_orig)
  903.                 num--;
  904.             if (on_y)
  905.                 *py = num;
  906.             else
  907.                 *px = num;
  908.             on_y = 1 - on_y;
  909.         }
  910.     }
  911.  
  912.     return Yes;
  913. }
  914.         
  915. /* 
  916.  * To move over characters by rewriting them, we have to check:
  917.  * (1) that the screen has been initialised on these positions;
  918.  * (2) we do not screw up characters or so_mode
  919.  * when rewriting linedata[y] from x_from upto x_to
  920.  */
  921. Hidden bool rewrite_ok(y, xfrom, xto)
  922.      int y;
  923.      int xfrom;
  924.      int xto;
  925. {
  926.     register char *plnyx, *pmdyx, *plnyto;
  927.     
  928.     if (xto > lenline[y])
  929.         return No;
  930.  
  931.     plnyto = &linedata[y][xto];
  932.     plnyx = &linedata[y][xfrom];
  933.     pmdyx = &linemode[y][xfrom];
  934.     while (plnyx <= plnyto) {
  935.         if (*plnyx == UNKNOWN
  936.             ||
  937.             (!has_xs && (*pmdyx != so_mode))
  938.            )
  939.             return No;
  940.         plnyx++, pmdyx++;
  941.     }
  942.     return Yes;
  943. }
  944.         
  945. /*
  946.  * Move to position y,x on the screen
  947.  */
  948. /* possible move types for y and x respectively: */
  949. #define None    0
  950. #define Down    1
  951. #define Up    2
  952. #define Right    1
  953. #define ReWrite    2
  954. #define Left    3
  955. #define CrWrite    4
  956.  
  957. Hidden Procedure move(y, x)
  958.      int y;
  959.      int x;
  960. {
  961.     int dy, dx;
  962.     int y_cost, x_cost, y_move, x_move;
  963.     int mode_cost;
  964.     int xi;
  965.     
  966.     if (cur_y == y && cur_x == x)
  967.         return;
  968.     
  969.     if (!has_mi || mode == Undefined)
  970.         set_mode(Normal);
  971.     if (!has_xs && ((!has_ms && so_mode != Off) || so_mode == Undefined))
  972.         standend();
  973.     
  974.     if (cur_y == Undefined || cur_x == Undefined)
  975.         goto absmove;
  976.     
  977.     dy = y - cur_y;
  978.     dx = x - cur_x;
  979.  
  980.     if (dy > 0) {
  981.         y_move = Down;
  982.         y_cost = dy * do_cost;
  983.     }
  984.     else if (dy < 0) {
  985.         y_move = Up;
  986.         y_cost = -dy * up_cost;
  987.     }
  988.     else {
  989.         y_move = None;
  990.         y_cost = 0;
  991.     }
  992.     if (y_cost < abs_cost) {
  993.         switch (mode) {
  994.         case Normal:
  995.             mode_cost = 0;
  996.             break;
  997.         case Insert:
  998.             mode_cost = ei_cost;
  999.             break;
  1000.         case Delete:
  1001.             mode_cost = ed_cost;
  1002.             break;
  1003.         }
  1004.         if (dx > 0) {
  1005.             x_cost = dx + mode_cost;
  1006.             if (dx*nd_cost < x_cost || !rewrite_ok(y, cur_x, x)) {
  1007.                 x_cost = dx * nd_cost;
  1008.                 x_move = Right;
  1009.             }
  1010.             else
  1011.                 x_move = ReWrite;
  1012.         }
  1013.         else if (dx < 0) {
  1014.             x_cost = -dx * le_cost;
  1015.             x_move = Left;
  1016.         }
  1017.         else {
  1018.             x_cost = 0;
  1019.             x_move = None;
  1020.         }
  1021.         if (cr_cost + x + mode_cost < x_cost && rewrite_ok(y, 0, x)) {
  1022.             x_move = CrWrite;
  1023.             x_cost = cr_cost + x + mode_cost;
  1024.         }
  1025.     }
  1026.     else
  1027.         x_cost = abs_cost;
  1028.  
  1029.     if (y_cost + x_cost < abs_cost) {
  1030.         switch (y_move) {
  1031.         case Down:
  1032.             while (dy-- > 0) Putstr(do_str);
  1033.             break;
  1034.         case Up:
  1035.             while (dy++ < 0) Putstr(up_str);
  1036.             break;
  1037.         }
  1038.         switch (x_move) {
  1039.         case Right:
  1040.             while (dx-- > 0) Putstr(nd_str);
  1041.             break;
  1042.         case Left:
  1043.             while (dx++ < 0) Putstr(le_str);
  1044.             break;
  1045.         case CrWrite:
  1046.             Putstr(cr_str);
  1047.             cur_x = 0;
  1048.             /* FALL THROUGH */
  1049.         case ReWrite:
  1050.             set_mode(Normal);
  1051.             for (xi = cur_x; xi < x; xi++)
  1052.                 fputc(linedata[y][xi], fp);
  1053.             break;
  1054.         }
  1055.     }
  1056.     else
  1057.     {
  1058.     absmove:
  1059.         if (cm_str == NULL) {
  1060.             Putstr(ho_str);
  1061.             for (cur_y = 0; cur_y < y; ++cur_y)
  1062.                 Putstr(do_str);
  1063.             /* Should try to use tabs here: */
  1064.             for (cur_x = 0; cur_x < x; ++cur_x)
  1065.                 Putstr(nd_str);
  1066.         }
  1067.         else
  1068.             Putstr(tgoto(cm_str, x, y));
  1069.     }
  1070.     
  1071.     cur_y = y;
  1072.     cur_x = x;
  1073. }
  1074.  
  1075.  
  1076. /*
  1077.  * Putting data on the screen.
  1078.  */
  1079.  
  1080. /*
  1081.  * Fill screen area with given "data".
  1082.  * Characters for which the corresponding char in "mode" have the value
  1083.  * STANDOUT must be put in inverse video.
  1084.  */
  1085. Visible Procedure trmputdata(yfirst, ylast, indent, data, mode)
  1086.      int yfirst, ylast;
  1087.      register int indent;
  1088.      register string data;
  1089.      register string mode;
  1090. {
  1091.     register int y;
  1092.     int x, len, lendata, space;
  1093.  
  1094. #ifdef VTRMTRACE
  1095.     if (vtrmfp) fprintf(vtrmfp, "\ttrmputdata(%d, %d, %d, \"%s\");\n", yfirst, ylast, indent, data);
  1096. #endif
  1097.     check_started("trmputdata");
  1098.     
  1099.     if (yfirst < 0)
  1100.         yfirst = 0;
  1101.     if (ylast >= lines)
  1102.         ylast = lines-1;
  1103.     space = cols*(ylast-yfirst+1) - indent;
  1104.     if (space <= 0)
  1105.         return;
  1106.     yfirst += indent/cols;
  1107.     indent %= cols;
  1108.     y= yfirst;
  1109.     if (!data)
  1110.         data= ""; /* Safety net */
  1111.     x = indent;
  1112.     lendata = strlen(data);
  1113.     if (ylast == lines-1 && lendata >= space)
  1114.         lendata = space - 1;
  1115.     len = Min(lendata, cols-x);
  1116.     while (y <= ylast) {
  1117.         put_line(y, x, data, mode, len);
  1118.         y++;
  1119.         lendata -= len;
  1120.         if (lendata > 0) {
  1121.             x = 0;
  1122.             data += len;
  1123.             if (mode != NULL)
  1124.                 mode += len;
  1125.             len = Min(lendata, cols);
  1126.         }
  1127.         else
  1128.             break;
  1129.     }
  1130.     if (y <= ylast)
  1131.         clear_lines(y, ylast);
  1132. }
  1133.  
  1134. /* 
  1135.  * We will first try to get the picture:
  1136.  *
  1137.  *                  op>>>>>>>>>>>op          oq<<<<<<<<<<<<<<<<<<<<<<<<oq
  1138.  *                  ^            ^           ^                         ^
  1139.  *           <xskip><-----m1----><----od-----><-----------m2----------->
  1140.  *   OLD:   "You're in a maze of twisty little pieces of code, all alike"
  1141.  *   NEW:          "in a maze of little twisting pieces of code, all alike"
  1142.  *                  <-----m1----><-----nd------><-----------m2----------->
  1143.  *                  ^            ^             ^                         ^
  1144.  *                  np>>>>>>>>>>>np            nq<<<<<<<<<<<<<<<<<<<<<<<<nq
  1145.  * where
  1146.  *    op, oq, np, nq are pointers to start and end of Old and New data,
  1147.  * and
  1148.  *    xskip = length of indent to be skipped,
  1149.  *    m1 = length of Matching part at start,
  1150.  *    od = length of Differing mid on screen,
  1151.  *    nd = length of Differing mid in data to be put,
  1152.  *    m2 = length of Matching trail.
  1153.  *
  1154.  * Then we will try to find a long blank-or-cleared piece in <nd+m2>:
  1155.  *
  1156.  *    <---m1---><---d1---><---nb---><---d2---><---m2--->
  1157.  *              ^         ^         ^        ^         ^
  1158.  *              np        bp        bq1      nq        nend
  1159.  * where
  1160.  *    bp, bq1 are pointers to start and AFTER end of blank piece,
  1161.  * and
  1162.  *    d1 = length of differing part before blank piece,
  1163.  *    nb = length of blank piece to be skipped,
  1164.  *    d2 = length of differing part after blank piece.
  1165.  * Remarks:
  1166.  *    d1 + nb + d2 == nd,
  1167.  * and
  1168.  *    d2 maybe less than 0.
  1169.  */
  1170. Hidden int put_line(y, xskip, data, mode, len)
  1171.      int y;
  1172.      int xskip;
  1173.      string data;
  1174.      string mode;
  1175.      int len;
  1176. {
  1177.     register char *op, *oq, *mp;
  1178.     register char *np, *nq, *nend, *mo;
  1179.     char *bp, *bq1, *p, *q;
  1180.     int m1, m2, od, nd, delta, dd, d1, nb, d2;
  1181.     bool skipping;
  1182.     int cost, o_cost;     /* normal and optimising cost */
  1183.     
  1184.     /* Bugfix GvR 19-June-87: */
  1185.     while (lenline[y] < xskip) {
  1186.         linedata[y][lenline[y]] = ' ';
  1187.         linemode[y][lenline[y]] = PLAIN;
  1188.         lenline[y]++;
  1189.     }
  1190.     
  1191.     /* calculate the magic parameters */
  1192.     op = &linedata[y][xskip];
  1193.     oq = &linedata[y][lenline[y]-1];
  1194.     mp = &linemode[y][xskip];
  1195.     np = data;
  1196.     nq = nend = data + len - 1;
  1197.     mo = (mode != NULL ? mode : plain);
  1198.     m1 = m2 = 0;
  1199.     while (op <= oq && np <= nq && *op == *np && ((*mp)&SOBIT) == *mo) {
  1200.         op++, np++, mp++, m1++;
  1201.         if (mode != NULL) mo++;
  1202.     }
  1203.     /* calculate m2, iff we can optimise or line keeps same length: */
  1204.     if (flags & CAN_OPTIMISE || (oq-op) == (nq-np)) {
  1205.         if (mode != NULL) mo = mode + len - 1;
  1206.         mp = &linemode[y][lenline[y]-1];
  1207.         while (op <= oq && np <= nq
  1208.                && *oq == *nq && ((*mp)&SOBIT) == *mo) {
  1209.             oq--, nq--, mp--, m2++;
  1210.             if (mode != NULL) mo--;
  1211.         }
  1212.     }
  1213.     od = oq - op + 1;
  1214.     nd = nq - np + 1;
  1215.     /* now we have the first picture above */
  1216.  
  1217.     if (od==0 && nd==0)
  1218.         return;
  1219.     delta = nd - od;
  1220.  
  1221.     /* find the blank piece */
  1222.     p = q = bp = bq1 = np;
  1223.     if (mode != NULL) mo = mode + (np - data);
  1224.     oq += m2;         /* back to current eol */
  1225.     if (delta == 0) /* if no change in linelength */
  1226.         nend -= m2;    /* don't find blanks in m2 */
  1227.     if (!has_in) {
  1228.         mp = &linemode[y][xskip + (op - (&linedata[y][xskip]))];
  1229.         while (p <= nend) {
  1230.             while (q <= nend && *q == ' ' && *mo == PLAIN
  1231.                    && (op > oq
  1232.                    || (*op == ' ' && ((*mp)&SOBIT) == NOCOOK)))
  1233.             {
  1234.                 q++, op++, mp++;
  1235.                 if (mode != NULL) mo++;
  1236.             }
  1237.             if (q - p > bq1 - bp)
  1238.                 bp = p, bq1 = q;
  1239.             p = ++q;
  1240.             if (mode != NULL) mo++;
  1241.             op++, mp++;
  1242.         }
  1243.     }
  1244.     d1 = bp - np;
  1245.     nb = bq1 - bp;
  1246.     d2 = nq - bq1 + 1;
  1247.     
  1248.     /* what is cheapest:
  1249.      * ([+m2] means: leave m2 alone if same linelength)
  1250.      *  normal: put nd[+m2];                       (dd = nd[+m2])
  1251.      *  skipping: put d1, skip nb, put d2[+m2];    (dd = d2[+m2])
  1252.      *  optimise: put dd, insert or delete delta.  (dd = min(od,nd))
  1253.      */
  1254.     cost = nd + (delta == 0 ? 0 : m2);     /* normal cost */
  1255.     if (nb > abs_cost || (d1 == 0 && nb > 0)) {
  1256.         skipping = Yes;
  1257.         cost -= nb - (d1>0 ? abs_cost : 0); /* skipping cost */
  1258.         dd = d2;
  1259.     }
  1260.     else {
  1261.         skipping = No;
  1262.         dd = nd;
  1263.     }
  1264.     
  1265.     if (m2 != 0 && delta != 0) {
  1266.         /* try optimising */
  1267.         o_cost = Min(od, nd);
  1268.         if (delta > 0)
  1269.             o_cost += delta * ins_mf + ins_oh;
  1270.         else if (delta < 0)
  1271.             o_cost += -delta * del_mf + del_oh;
  1272.         if (o_cost >= cost) {
  1273.             /* discard m2, no optimise */
  1274.             dd += m2;
  1275.             m2 = 0;
  1276.         }
  1277.         else {
  1278.             dd = Min(od, nd);
  1279.             skipping = No;
  1280.         }
  1281.     }
  1282.  
  1283.     /* and now for the real work */
  1284.     if (!skipping || d1 > 0)
  1285.         move(y, xskip + m1);
  1286.  
  1287.     if (has_xs)
  1288.         get_so_mode();
  1289.     
  1290.     if (skipping) {
  1291.         if (d1 > 0) {
  1292.             set_mode(Normal);
  1293.             mo= (mode != NULL ? mode+(np-data) : NULL);
  1294.             put_str(np, mo, d1, No);
  1295.         }
  1296.         if (has_xs && so_mode != Off)
  1297.             standend();
  1298.         set_blanks(y, xskip+m1+d1, xskip+m1+d1+nb);
  1299.         if (dd != 0 || delta < 0) {
  1300.             move(y, xskip+m1+d1+nb);
  1301.             np = bq1;
  1302.         }
  1303.     }
  1304.     
  1305.     if (dd > 0) {
  1306.         set_mode(Normal);
  1307.         mo= (mode != NULL ? mode+(np-data) : NULL);
  1308.         put_str(np, mo, dd, No);
  1309.     }
  1310.     
  1311.     if (m2 > 0) {
  1312.         if (delta > 0) {
  1313.             set_mode(Insert);
  1314.             mo= (mode != NULL ? mode+(np+dd-data) : NULL);
  1315.             ins_str(np+dd, mo, delta);
  1316.         }
  1317.         else if (delta < 0) {
  1318.             if (so_mode != Off)
  1319.                 standend();
  1320.                 /* Some terminals fill with standout spaces! */
  1321.             set_mode(Delete);
  1322.             del_str(-delta);
  1323.         }
  1324.     }
  1325.     else {
  1326.         if (delta < 0) {
  1327.             clr_to_eol();
  1328.             return;
  1329.         }
  1330.     }
  1331.     
  1332.     lenline[y] = xskip + len;
  1333.     if (cur_x == cols) {
  1334.         if (!has_mi)
  1335.             set_mode(Normal);
  1336.         if (!has_ms)
  1337.             so_mode = Undefined;
  1338.         if (has_am) {
  1339.             if (has_xn)
  1340.                 cur_y= Undefined;
  1341.             else
  1342.                 cur_y++;
  1343.         }
  1344.         else
  1345.             Putstr(cr_str);
  1346.         cur_x = 0;
  1347.     }
  1348.     else if (has_xs) {
  1349.         if (m2 == 0) {
  1350.             if (so_mode == On)
  1351.                 standend();
  1352.         }
  1353.         else {
  1354.             if (!(linemode[cur_y][cur_x] & XSBIT)) {
  1355.                 if (so_mode != (linemode[cur_y][cur_x] & SOBIT))
  1356.                     (so_mode ? standend() : standout());
  1357.             }
  1358.         }
  1359.     }
  1360. }
  1361.  
  1362. Hidden Procedure set_mode(m)
  1363.      int m;
  1364. {
  1365.     if (m == mode)
  1366.         return;
  1367.     switch (mode) {
  1368.     case Insert:
  1369.         Putstr(ei_str);
  1370.         break;
  1371.     case Delete:
  1372.         Putstr(ed_str);
  1373.         break;
  1374.     case Undefined:
  1375.         Putstr(ei_str);
  1376.         Putstr(ed_str);
  1377.         break;
  1378.     }
  1379.     switch (m) {
  1380.     case Insert:
  1381.         Putstr(im_str);
  1382.         break;
  1383.     case Delete:
  1384.         Putstr(dm_str);
  1385.         break;
  1386.     }
  1387.     mode = m;
  1388. }
  1389.  
  1390. Hidden Procedure get_so_mode()
  1391. {
  1392.     if (cur_x >= lenline[cur_y] || linemode[cur_y][cur_x] == NOCOOK)
  1393.         so_mode = Off;
  1394.     else
  1395.         so_mode = linemode[cur_y][cur_x] & SOBIT;
  1396. }
  1397.  
  1398. Hidden Procedure standout()
  1399. {
  1400.     Putstr(so_str);
  1401.     so_mode = On;
  1402.     if (has_xs)
  1403.         linemode[cur_y][cur_x] = SOCOOK;
  1404. }
  1405.  
  1406. Hidden Procedure standend()
  1407. {
  1408.     Putstr(se_str);
  1409.     so_mode = Off;
  1410.     if (has_xs)
  1411.         linemode[cur_y][cur_x] = SECOOK;
  1412. }
  1413.  
  1414. Hidden Procedure put_str(data, mode, n, inserting)
  1415.      char *data;
  1416.      char *mode;
  1417.      int n;
  1418.      bool inserting;
  1419. {
  1420.     register char ch, mo;
  1421.     register short so;
  1422.     char *ln_y_x, *ln_y_end;
  1423.     
  1424.     so = so_mode;
  1425.     if (has_xs) {
  1426.         ln_y_x = &linemode[cur_y][cur_x];
  1427.         ln_y_end = &linemode[cur_y][lenline[cur_y]];
  1428.     }
  1429.     while (n-- > 0) {
  1430.         if (has_xs && ln_y_x <= ln_y_end && ((*ln_y_x)&XSBIT))
  1431.             so = so_mode = (*ln_y_x)&SOBIT;
  1432.             /* this also checks for the standend cookie AFTER */
  1433.             /* the line because off the equals sign in <= */
  1434.         ch= *data++;
  1435.         mo= (mode != NULL ? *mode++ : PLAIN);
  1436.         if (mo != so) {
  1437.             so = mo;
  1438.             so ? standout() : standend();
  1439.         }
  1440.         if (inserting)
  1441.             Putstr(ic_str);
  1442.         put_c(ch, mo);
  1443.         if (has_xs)
  1444.             ln_y_x++;
  1445.     }
  1446. }
  1447.  
  1448. Hidden Procedure ins_str(data, mode, n)
  1449.      char *data;
  1450.      char *mode;
  1451.      int n;
  1452. {
  1453.     int x;
  1454.     
  1455.     /* x will start AFTER the line, because there might be a cookie */
  1456.     for (x = lenline[cur_y]; x >= cur_x; x--) {
  1457.         linedata[cur_y][x+n] = linedata[cur_y][x];
  1458.         linemode[cur_y][x+n] = linemode[cur_y][x];
  1459.     }
  1460.     put_str(data, mode, n, Yes);
  1461. }
  1462.  
  1463. Hidden Procedure del_str(n)
  1464.      int n;
  1465. {
  1466.     int x, xto;
  1467.     
  1468.     xto = lenline[cur_y] - n; /* again one too far because of cookie */
  1469.     if (has_xs) {
  1470.         for (x = cur_x + n; x >= cur_x; x--) {
  1471.             if (linemode[cur_y][x] & XSBIT)
  1472.                 break;
  1473.         }
  1474.         if (x >= cur_x)
  1475.             linemode[cur_y][cur_x+n] = linemode[cur_y][x];
  1476.     }
  1477.     for (x = cur_x; x <= xto; x++) {
  1478.         linedata[cur_y][x] = linedata[cur_y][x+n];
  1479.         linemode[cur_y][x] = linemode[cur_y][x+n];
  1480.     }
  1481.     while (n-- > 0)
  1482.         Putstr(dc_str);
  1483. }
  1484.  
  1485. Hidden Procedure put_c(ch, mo)
  1486.      char ch;
  1487.      char mo;
  1488. {
  1489.     char sobit, xs_flag;
  1490.  
  1491.     fputc(ch, fp);
  1492.     sobit = (mo == STANDOUT ? SOBIT : 0);
  1493.     if (has_xs)
  1494.         xs_flag = linemode[cur_y][cur_x]&XSBIT;
  1495.     else
  1496.         xs_flag = 0;
  1497.     linedata[cur_y][cur_x] = ch;
  1498.     linemode[cur_y][cur_x] = sobit|xs_flag;
  1499.     cur_x++;
  1500. }
  1501.  
  1502. Hidden Procedure clear_lines(yfirst, ylast)
  1503.      int yfirst;
  1504.      int ylast;
  1505. {
  1506.     register int y;
  1507.     
  1508.     if (!has_xs && so_mode != Off)
  1509.         standend();
  1510.     if (cl_str && yfirst == 0 && ylast == lines-1) {
  1511.         Putstr(cl_str);
  1512.         cur_y = cur_x = 0;
  1513.         for (y = 0; y < lines; ++y) {
  1514.             lenline[y] = 0;
  1515.             if (has_xs) linemode[y][0] = NOCOOK;
  1516.         }
  1517.         return;
  1518.     }
  1519.     for (y = yfirst; y <= ylast; y++) {
  1520.         if (lenline[y] > 0) {
  1521.             move(y, 0);
  1522.             if (ylast == lines-1 && cd_str) {
  1523.                 Putstr(cd_str);
  1524.                 while (y <= ylast) {
  1525.                     if (has_xs) linemode[y][0] = NOCOOK;
  1526.                     lenline[y++] = 0;
  1527.                 }
  1528.                 break;
  1529.             }
  1530.             else {
  1531.                 clr_to_eol();
  1532.             }
  1533.         }
  1534.     }
  1535. }
  1536.  
  1537. Hidden Procedure clr_to_eol()
  1538. {
  1539.     lenline[cur_y] = cur_x;
  1540.     if (!has_xs && so_mode != Off)
  1541.         standend();
  1542.     Putstr(ce_str);
  1543.     if (has_xs) {
  1544.         if (cur_x == 0)
  1545.             linemode[cur_y][0] = NOCOOK;
  1546.         else if (linemode[cur_y][cur_x-1]&SOBIT)
  1547.             standend();
  1548.     }
  1549. }
  1550.  
  1551. Hidden Procedure set_blanks(y, xfrom, xto)
  1552.      int y;
  1553.      int xfrom;
  1554.      int xto;
  1555. {
  1556.     register int x;
  1557.     
  1558.     for (x = xfrom; x < xto; x++) {
  1559.         linedata[y][x] = ' '; 
  1560.         if (has_xs && lenline[y] >= x && linemode[y][x]&XSBIT)
  1561.             linemode[y][x]= SECOOK; 
  1562.         else
  1563.             linemode[y][x]= NOCOOK;
  1564.     }
  1565. }
  1566.  
  1567. /* 
  1568.  * outchar() is used by termcap's tputs.
  1569.  */
  1570. Hidden int outchar(ch)
  1571.      char ch;
  1572. {
  1573.     fputc(ch, fp);
  1574. }
  1575.  
  1576. /*
  1577.  * Scrolling (part of) the screen up (or down, by<0).
  1578.  */
  1579.  
  1580. Visible Procedure trmscrollup(yfirst, ylast, by)
  1581.      register int yfirst;
  1582.      register int ylast;
  1583.      register int by;
  1584. {
  1585. #ifdef VTRMTRACE
  1586.     if (vtrmfp) fprintf(vtrmfp, "\ttrmscrollup(%d, %d, %d);\n", yfirst, ylast, by);
  1587. #endif
  1588.     check_started("trmscrollup");
  1589.     
  1590.     if (by == 0)
  1591.         return;
  1592.  
  1593.     if (yfirst < 0)
  1594.         yfirst = 0;
  1595.     if (ylast >= lines)
  1596.         ylast = lines-1;
  1597.  
  1598.     if (yfirst > ylast)
  1599.         return;
  1600.  
  1601.     if (!has_xs && so_mode != Off)
  1602.         standend();
  1603.     
  1604.     if (by > 0 && yfirst + by > ylast
  1605.         ||
  1606.         by < 0 && yfirst - by > ylast)
  1607.     {
  1608.         clear_lines(yfirst, ylast);
  1609.         return;
  1610.     }
  1611.     
  1612.     if (canscroll) {
  1613.         if (by > 0) {
  1614.             (*scr_up)(yfirst, ylast, by);
  1615.             scr_lines(yfirst, ylast, by, 1);
  1616.         }
  1617.         else if (by < 0) {
  1618.             (*scr_down)(yfirst, ylast, -by);
  1619.             scr_lines(ylast, yfirst, -by, -1);
  1620.         }
  1621.     }
  1622.     else {
  1623.         scr3up(yfirst, ylast, by);
  1624.     }
  1625.  
  1626.     VOID fflush(fp);
  1627. }
  1628.  
  1629. Hidden Procedure scr_lines(yfrom, yto, n, dy)
  1630.      int yfrom;
  1631.      int yto;
  1632.      int n;
  1633.      int dy;
  1634. {
  1635.     register int y;
  1636.     char *savedata;
  1637.     char *savemode;
  1638.     
  1639.     while (n-- > 0) {
  1640.         savedata = linedata[yfrom];
  1641.         savemode = linemode[yfrom];
  1642.         for (y = yfrom; y != yto; y += dy) {
  1643.             linedata[y] = linedata[y+dy];
  1644.             linemode[y] = linemode[y+dy];
  1645.             lenline[y] = lenline[y+dy];
  1646.         }
  1647.         linedata[yto] = savedata;
  1648.         linemode[yto] = savemode;
  1649.         lenline[yto] = 0;
  1650.         if (has_xs) linemode[yto][0] = NOCOOK;
  1651.     }
  1652. }
  1653.  
  1654. Hidden Procedure scr1up(yfirst, ylast, n)
  1655.      int yfirst;
  1656.      int ylast;
  1657.      int n;
  1658. {
  1659.     move(yfirst, 0);
  1660.     dellines(n);
  1661.     if (ylast < lines-1) {
  1662.         move(ylast-n+1, 0);
  1663.         addlines(n);
  1664.     }
  1665. }
  1666.  
  1667.  
  1668. Hidden Procedure scr1down(yfirst, ylast, n)
  1669.      int yfirst;
  1670.      int ylast;
  1671.      int n;
  1672. {
  1673.     if (ylast == lines-1) {
  1674.         clear_lines(ylast-n+1, ylast);
  1675.     }
  1676.     else {
  1677.         move(ylast-n+1, 0);
  1678.         dellines(n);
  1679.     }
  1680.     move(yfirst, 0);
  1681.     addlines(n);
  1682. }
  1683.  
  1684.  
  1685. Hidden Procedure addlines(n)
  1686.      register int n;
  1687. {
  1688.     if (par_al_str && n > 1)
  1689.             Putstr(tgoto(par_al_str, n, n));
  1690.     else {
  1691.         while (n-- > 0)
  1692.             Putstr(al_str);
  1693.     }
  1694. }
  1695.  
  1696.  
  1697. Hidden Procedure dellines(n)
  1698.      register int n;
  1699. {
  1700.     if (par_dl_str && n > 1)
  1701.         Putstr(tgoto(par_dl_str, n, n));
  1702.     else {
  1703.         while (n-- > 0)
  1704.             Putstr(dl_str);
  1705.     }
  1706. }
  1707.  
  1708.  
  1709. Hidden Procedure scr2up(yfirst, ylast, n)
  1710.      int yfirst;
  1711.      int ylast;
  1712.      int n;
  1713. {
  1714.     Putstr(tgoto(cs_str, ylast, yfirst));
  1715.     cur_y = cur_x = Undefined;
  1716.     move(ylast, 0);
  1717.     while (n-- > 0) {
  1718.         Putstr(sf_str);
  1719.         if (has_db && ylast == lines-1)
  1720.             clr_to_eol();
  1721.     }
  1722.     Putstr(tgoto(cs_str, lines-1, 0));
  1723.     cur_y = cur_x = Undefined;
  1724. }
  1725.  
  1726.  
  1727. Hidden Procedure scr2down(yfirst, ylast, n)
  1728.      int yfirst;
  1729.      int ylast;
  1730.      int n;
  1731. {
  1732.     Putstr(tgoto(cs_str, ylast, yfirst));
  1733.     cur_y = cur_x = Undefined;
  1734.     move(yfirst, 0);
  1735.     while (n-- > 0) {
  1736.         Putstr(sr_str);
  1737.         if (has_da && yfirst == 0)
  1738.             clr_to_eol();
  1739.     }
  1740.     Putstr(tgoto(cs_str, lines-1, 0));
  1741.     cur_y = cur_x = Undefined;
  1742. }
  1743.  
  1744. /* for dumb scrolling, uses and updates internal administration */
  1745.  
  1746. Hidden Procedure scr3up(yfirst, ylast, by)
  1747.      int yfirst;
  1748.      int ylast;
  1749.      int by;
  1750. {
  1751.     if (by > 0 && yfirst == 0) {
  1752.         lf_scroll(ylast, by);
  1753.     }
  1754.     else if (by > 0) {
  1755.         move_lines(yfirst+by, yfirst, ylast-yfirst+1-by, 1);
  1756.         clear_lines(ylast-by+1, ylast);
  1757.     }
  1758.     else {
  1759.         move_lines(ylast+by, ylast, ylast-yfirst+1+by, -1);
  1760.         clear_lines(yfirst, yfirst-by-1);
  1761.     }
  1762. }
  1763.  
  1764. Hidden Procedure lf_scroll(yto, by)
  1765.      int yto;
  1766.      int by;
  1767. {
  1768.     register int n = by;
  1769.  
  1770.     move(lines-1, 0);
  1771.     while (n-- > 0) {
  1772.         fputc('\n', fp);
  1773.     }
  1774.     scr_lines(0, lines-1, by, 1);
  1775.     move_lines(lines-1-by, lines-1, lines-1-yto, -1);
  1776.     clear_lines(yto-by+1, yto);
  1777. }
  1778.  
  1779. Hidden Procedure move_lines(yfrom, yto, n, dy)
  1780.      int yfrom;
  1781.      int yto;
  1782.      int n;
  1783.      int dy;
  1784. {
  1785.     while (n-- > 0) {
  1786.         put_line(yto, 0, linedata[yfrom], linemode[yfrom], lenline[yfrom]);
  1787.         yfrom += dy;
  1788.         yto += dy;
  1789.     }
  1790. }
  1791.  
  1792. /*
  1793.  * Synchronization, move cursor to given position (or previous if < 0).
  1794.  */
  1795.  
  1796. Visible Procedure trmsync(y, x)
  1797.      int y;
  1798.      int x;
  1799. {
  1800. #ifdef VTRMTRACE
  1801.     if (vtrmfp) fprintf(vtrmfp, "\ttrmsync(%d, %d);\n", y, x);
  1802. #endif
  1803.     check_started("trmsync");
  1804.     
  1805.     if (0 <= y && y < lines && 0 <= x && x < cols) {
  1806.         move(y, x);
  1807.         if (no_cursor) {
  1808.             Putstr(ve_str);
  1809.             no_cursor = No;
  1810.         }
  1811.     }
  1812.     else if (no_cursor == No) {
  1813.         Putstr(vi_str);
  1814.         no_cursor = Yes;
  1815.     }
  1816.     VOID fflush(fp);
  1817. }
  1818.  
  1819.  
  1820. /*
  1821.  * Send a bell, visible if possible.
  1822.  */
  1823.  
  1824. Visible Procedure trmbell()
  1825. {
  1826. #ifdef VTRMTRACE
  1827.     if (vtrmfp) fprintf(vtrmfp, "\ttrmbell();\n");
  1828. #endif
  1829.     check_started("trmbell");
  1830.     
  1831.     Putstr(vb_str);
  1832.     VOID fflush(fp);
  1833. }
  1834.  
  1835.  
  1836. #ifdef SHOW
  1837.  
  1838. /*
  1839.  * Show the current internal statuses of the screen on vtrmfp.
  1840.  * For debugging only.
  1841.  */
  1842.  
  1843. Visible Procedure trmshow(s)
  1844.      char *s;
  1845. {
  1846.     int y, x;
  1847.     
  1848.     if (!vtrmfp)
  1849.         return;
  1850.     fprintf(vtrmfp, "<<< %s >>>\n", s);
  1851.     for (y = 0; y < lines; y++) {
  1852.         for (x = 0; x <= lenline[y] /*** && x < cols-1 ***/ ; x++) {
  1853.             fputc(linedata[y][x], vtrmfp);
  1854.         }
  1855.         fputc('\n', vtrmfp);
  1856.         for (x = 0; x <= lenline[y] && x < cols-1; x++) {
  1857.             if (linemode[y][x]&SOBIT)
  1858.                 fputc('-', vtrmfp);
  1859.             else
  1860.                 fputc(' ', vtrmfp);
  1861.         }
  1862.         fputc('\n', vtrmfp);
  1863.         for (x = 0; x <= lenline[y] && x < cols-1; x++) {
  1864.             if (linemode[y][x]&XSBIT)
  1865.                 fputc('+', vtrmfp);
  1866.             else
  1867.                 fputc(' ', vtrmfp);
  1868.         }
  1869.         fputc('\n', vtrmfp);
  1870.     }
  1871.     fprintf(vtrmfp, "CUR_Y = %d, CUR_X = %d.\n", cur_y, cur_x);
  1872.     VOID fflush(vtrmfp);
  1873. }
  1874. #endif
  1875.  
  1876.  
  1877. /*
  1878.  * Return the next input character, or -1 if read fails.
  1879.  * Only the low 7 bits are returned, so reading in RAW mode is permissible
  1880.  * (although CBREAK is preferred if implemented).
  1881.  * To avoid having to peek in the input buffer for trmavail, we use the
  1882.  * 'read' system call rather than getchar().
  1883.  * (The interface allows 8-bit characters to be returned, to accomodate
  1884.  * larger character sets!)
  1885.  */
  1886.  
  1887. Hidden int pushback= -1;
  1888.  
  1889. Visible int trminput()
  1890. {
  1891.     char c;
  1892.     int n;
  1893.  
  1894. #ifdef CATCHINTR
  1895.     if (trmintrptd) {
  1896.         trmintrptd= No;
  1897.         return intrchar & 0377;
  1898.     }
  1899. #ifdef SETJMP
  1900.     if (setjmp(readinterrupt) != 0) {
  1901.         readintrcontext = No;
  1902.         trmintrptd = No;
  1903.         return intrchar & 0377;
  1904.     }
  1905. #endif
  1906. #endif /* CATCHINTR */
  1907.  
  1908.     if (pushback >= 0) {
  1909.         c= pushback;
  1910.         pushback= -1;
  1911.         return c;
  1912.     }
  1913. #ifdef CATCHINTR
  1914.     readintrcontext = Yes;
  1915. #endif
  1916.  
  1917.     n= read(0, &c, 1);
  1918.  
  1919. #ifdef CATCHINTR
  1920.     readintrcontext = No;
  1921. #endif
  1922.     if (n <= 0)
  1923.         return -1;
  1924.     return c & 0377;
  1925. }
  1926.  
  1927. Hidden Procedure trmpushback(c)
  1928.      int c;
  1929. {
  1930.     pushback= c;
  1931. }
  1932.  
  1933.  
  1934. /*
  1935.  * See if there's input available from the keyboard.
  1936.  * The code to do this is dependent on the type of Unix you have
  1937.  * (BSD, System V, ...).
  1938.  * Return value: 0 -- no input; 1 -- input; -1 -- unimplementable.
  1939.  * Note that each implementation form should first check pushback.
  1940.  *
  1941.  * TO DO:
  1942.  *    - Implement it for other than 4.x BSD! (notably System 5)
  1943.  */
  1944.  
  1945. #ifdef HAS_SELECT
  1946.  
  1947. Hidden int dep_trmavail()
  1948. {
  1949.     int nfound, nfds, readfds;
  1950.     static struct timeval timeout= {0, 0};
  1951.  
  1952.     readfds= 1 << 0;
  1953.     nfds= 0+1;
  1954.     nfound= select(nfds, &readfds, (int*) 0, (int*) 0, &timeout);
  1955.     return nfound > 0;
  1956. }
  1957.  
  1958. #define TRMAVAIL_DEFINED
  1959.  
  1960. #else /* !HAS_SELECT */
  1961.  
  1962. #ifdef HAS_TERMIO
  1963.  
  1964. Hidden int dep_trmavail()
  1965. {
  1966.     int n;
  1967.     char c;
  1968.     
  1969.     stty(0, &polltty);
  1970.     n= read(0, &c, 1);
  1971.     if (n == 1)
  1972.         trmpushback((int)c);
  1973.     stty(0, &newtty);
  1974.     return n > 0;
  1975. }
  1976.  
  1977. #define TRMAVAIL_DEFINED
  1978.  
  1979. #endif /* HAS_TERMIO */
  1980.  
  1981. #endif /* HAS_SELECT */
  1982.  
  1983. #ifndef TRMAVAIL_DEFINED
  1984.  
  1985. Hidden int dep_trmavail()
  1986. {
  1987.     return -1;
  1988. }
  1989.  
  1990. #endif
  1991.  
  1992. Visible int trmavail()
  1993. {
  1994. #ifdef CATCHINTR
  1995.     if (trmintrptd) return 1;
  1996. #endif
  1997.     if (pushback >= 0)
  1998.         return 1;
  1999.     return dep_trmavail(); /* dependent code */
  2000. }    
  2001.  
  2002. /*
  2003.  * Suspend the interpreter or editor.
  2004.  * Should be called only after trmend and before trmstart!
  2005.  */
  2006.  
  2007. Visible int trmsuspend()
  2008. {
  2009. #ifdef SIGTSTP
  2010.     SIGTYPE (*oldsig)();
  2011.     
  2012.     oldsig= signal(SIGTSTP, SIG_IGN);
  2013.     if (oldsig == SIG_IGN) {
  2014.         return subshell();
  2015.     }
  2016.     signal(SIGTSTP, oldsig);
  2017.     kill(0, SIGSTOP);
  2018.     return 1;
  2019. #else /*SIGTSTP*/
  2020.     return subshell();
  2021. #endif /*SIGTSTP*/
  2022. }
  2023.  
  2024. Hidden int subshell()
  2025. {
  2026.     int r;
  2027.  
  2028.     r= system("exec ${SHELL-/bin/sh}");
  2029.     if (r == -1 || r == 127)
  2030.         return 0;
  2031.     else
  2032.         return 1;
  2033. }
  2034.  
  2035.  
  2036. /*
  2037.  * DESCRIPTION.
  2038.  *
  2039.  * This package uses termcap to determine the terminal capabilities.
  2040.  *
  2041.  * The lines and columns of our virtual terminal are numbered 
  2042.  *    y = {0...lines-1} from top to bottom, and
  2043.  *    x = {0...cols-1} from left to right,
  2044.  * respectively.
  2045.  *
  2046.  * The Visible Procedures in this package are:
  2047.  *
  2048.  * trmstart(&lines, &cols, &flags)
  2049.  *     Obligatory initialization call (sets tty modes etc.),
  2050.  *     Returns the height and width of the screen to the integers
  2051.  *     whose addresses are passed as parameters, and a flag that
  2052.  *    describes some capabilities (see vtrm.h).
  2053.  *    Function return value: 0 if all went well, an error code if there
  2054.  *    is any trouble.  No messages are printed for errors.
  2055.  *
  2056.  * trmundefined()
  2057.  *      States that the screen contents are no longer valid.
  2058.  *      This is necessary for a hard redraw, for instance.
  2059.  *
  2060.  * trmsense(sense, format, &y, &x)
  2061.  *    Returns the cursor position through its parameters, for
  2062.  *      instance after a mouse click. Parameter 'sense' is a string
  2063.  *      that is sent to the terminal, if that is necessary or NULL if
  2064.  *      not. Parameter 'format' is a string that defines the format of
  2065.  *      the reply from the terminal.
  2066.  *
  2067.  * trmputdata(yfirst, ylast, indent, data)
  2068.  *     Fill lines {yfirst..ylast} with data, after skipping the initial
  2069.  *    'indent' positions. It is assumed that these positions do not contain
  2070.  *    anything dangerous (like standout cookies or null characters).
  2071.  *
  2072.  * trmscrollup(yfirst, ylast, by)
  2073.  *     Shift lines {yfirst..ylast} up by lines (down |by| if by < 0).
  2074.  *
  2075.  * trmsync(y, x)
  2076.  *     Output data to the terminal and set cursor position to (y, x).
  2077.  *
  2078.  * trmbell()
  2079.  *    Send a (possibly visible) bell immediately.
  2080.  *
  2081.  * trmend()
  2082.  *     Obligatory termination call (resets tty modes etc.).
  2083.  *
  2084.  *      You may call these as one or more cycles of:
  2085.  *             + trmstart
  2086.  *             +    zero or more times any of the other routines
  2087.  *             + trmend
  2088.  *      Trmend may be called even in the middle of trmstart; this is necessary
  2089.  *      to make it possible to write an interrupt handler that resets the tty
  2090.  *      state before exiting the program.
  2091.  *
  2092.  * ADDITIONAL SPECIFICATIONS (ROUTINES FOR CHARACTER INPUT)
  2093.  *
  2094.  * trminput()
  2095.  *    Return the next input character (with its parity bit cleared
  2096.  *    if any).  This value is a nonnegative int.  Returns -1 if the
  2097.  *    input can't be read any more.
  2098.  *
  2099.  * trmavail()
  2100.  *    Return 1 if there is an input character immediately available,
  2101.  *    0 if not.  Return -1 if not implementable.
  2102.  *
  2103.  * trminterrupt()
  2104.  *    Return 1 if an interrupt has occurred since the last call to
  2105.  *    trminput or trmavail, 0 else.  [Currently not implemented.]
  2106.  *
  2107.  * trmsuspend()
  2108.  *    When called in the proper environment (4BSD with job control
  2109.  *    enabled), suspends the editor, temporarily popping back to
  2110.  *    the calling shell.  The caller should have called trmend()
  2111.  *    first, and must call trmstart again afterwards.
  2112.  *    BUG: there is a timing window where keyboard-generated
  2113.  *    signals (such as interrupt) can reach the program.
  2114.  *    In non 4BSD we try to spawn a subshell.
  2115.  *    Returns 1 if suspend succeeded, 0 if it failed.
  2116.  *    (-1 iff not implementable).
  2117.  */
  2118.